import { cloneDeep } from "lodash";
import { cmdObj, TaskCmd } from "../../constants/command";
import { logger } from "../log";
import { BuildTaskResult } from "../model";
import { BuildTaskRecord, BuildTaskStatus } from "../model/TaskRecordDBSchema";
import { WsMsgType } from "../model/WebSocket";
import { buildTaskResultService } from "../service/BuildTaskResultService";
import { buildTaskService } from "../service/BuildTaskService";
import { sendToClientAll } from "../ws/sendToClient";
import { taskExecutor } from "./TaskExecutor";

const TimeStatsRegExp: Record<keyof BuildTaskResult["timeStats"], RegExp> = {
  loaded: /---Load\s+?Cost\s+?(\d+(\.\d+)?)\s+?ms---/,
  compiled: /---Compile\s+?Cost\s+?(\d+(\.\d+)?)\s+?ms---/,
  total: /---Total\s+?Cost\s+?(\d+(\.\d+)?)\s+?ms---/,
};

/**
 *
 * @param taskParam
 * @param alternate 是否交替执行
 * @returns
 */
function* genTask(taskParam: BuildTaskRecord, alternate = true) {
  if (taskParam.status === "finish" || taskParam.rest <= 0) return null;

  const taskList = taskParam.taskList;
  if (!alternate) {
    for (const task of taskList) {
      let rest = task.rest || 0;
      while (rest > 0) {
        // 获取对应执行命令
        yield cmdObj[task.type]!;
        rest--;
      }
    }
  } else {
    // 交替执行(层序遍历)
    const maxLen = taskList.reduce((pre, cur) => {
      return Math.max(pre, cur.rest);
    }, 0);

    let i = 0;
    while (i < maxLen) {
      for (const task of taskList) {
        if (i < task.rest) {
          yield cmdObj[task.type]!;
        }
      }
      i++;
    }
  }
  return null;
}

/**
 * 保留小数位数
 * @param numStr
 * @returns
 */
function fixNum(numStr: string): number {
  if (!numStr) return 0;
  return parseFloat(parseFloat(numStr).toFixed(4));
}

function parseTimeStats(str: string): Partial<BuildTaskResult["timeStats"]> {
  if (!str || !str.includes("ms---")) return {};
  const rslt: Partial<BuildTaskResult["timeStats"]> = {};
  Object.keys(TimeStatsRegExp).map((v) => {
    // 尝试获取加载时间
    let match = TimeStatsRegExp[v as keyof typeof TimeStatsRegExp].exec(str);
    if (match?.[1]) {
      rslt[v as keyof typeof TimeStatsRegExp] = fixNum(match[1]);
    }
  });
  return rslt;
}

/**
 * 负责自动或者手动检查是否有可执行的任务;检查之前先判断任务执行器是否在运行。
 */
export class TaskScheduler {
  #status: "idle" | "working" = "idle";
  #curTask: BuildTaskRecord | null = null;

  constructor() {
    logger.info("---init task scheduler--");
  }

  async #getRunnableTask() {
    return Promise.all([
      buildTaskService.find((v) => {
        return v.status === "running";
      }),
      buildTaskService.find((v) => {
        return v.status === "pending";
      }),
    ]).then((v) => v.filter((val) => val)[0]);
  }

  workingTask() {
    return this.#curTask;
  }

  isWorking() {
    return this.#status === "working";
  }

  async #run(task: Generator<TaskCmd, null, never>, taskInfo: BuildTaskRecord) {
    const cmdObj = task.next().value;
    if (!cmdObj) {
      // 任务运行完成
      this.#status = "idle";
      const rslt = {
        taskId: taskInfo.taskId,
        status: "finish" as BuildTaskStatus,
        endTime: Date.now(),
      };
      buildTaskService.updateTaskStatus(rslt);
      // 通知websocket
      sendToClientAll({ type: WsMsgType.TASK_UPDATE, payload: rslt });
      logger.info(`--- task ${taskInfo.taskId} ${taskInfo.taskTitle} end ---`);
      this.#curTask = null;
      // 尝试触发下一个任务执行
      this.notify();
      return;
    }

    const start = Date.now();
    logger.info(
      `Task ${taskInfo.taskId} ${taskInfo.taskTitle} ${cmdObj.type} start.`
    );
    const timeStats: BuildTaskResult["timeStats"] = {
      loaded: 0,
      compiled: 0,
      total: 0,
    };
    let status: "success" | "fail" = "success";
    try {
      await taskExecutor.exec(cmdObj, {
        onData(str) {
          const rslt = parseTimeStats(str);
          Object.assign(timeStats, rslt);
        },
      });
      status = "success";
      logger.info(
        `Task ${taskInfo.taskId} ${taskInfo.taskTitle} ${cmdObj.type} success.`
      );
    } catch (e) {
      logger.error(`Task ${taskInfo.taskId} ${taskInfo.taskTitle} error!`);
      status = "fail";
    } finally {
      // 如果没有打印总数，进行补充
      if (!timeStats.total) {
        timeStats.total = Date.now() - start;
      }

      // 更新结果记录
      const rslt: BuildTaskResult = {
        start,
        end: Date.now(),
        status,
        timeStats,
        type: cmdObj.type,
        taskId: taskInfo.taskId,
      };
      await buildTaskResultService.add(rslt);
      // 通知websocket
      sendToClientAll({
        type: WsMsgType.TASK_RESULT_UPDATE,
        payload: rslt,
      });

      // 更新任务记录
      await buildTaskService.updateTaskRest({
        taskId: taskInfo.taskId,
        type: cmdObj.type,
        status,
      });
      // 继续下一次任务
      this.#run(task, taskInfo);
    }
  }

  /**
   * 尝试唤起任务调度
   */
  async notify() {
    if (this.isWorking()) return;
    // 尝试获取可执行的任务参数
    const task = await this.#getRunnableTask();
    if (!task) {
      logger.info(`No task to start. Waiting...`);
      return;
    }
    this.#status = "working";
    this.#curTask = task;
    logger.info(
      `--- task ${task.taskId} ${task.taskTitle} start, total: ${task.total} ---`
    );
    // 更新任务状态
    if (task.status !== "running") {
      await buildTaskService.updateTaskStatus({
        taskId: task.taskId,
        status: "running",
        startTime: Date.now(),
      });
    }
    // 使用迭代器不断取出任务进行执行
    this.#run(genTask(task), task);
  }
}

export const taskScheduler = new TaskScheduler();
